package com.lcf.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.activerecord.AbstractModel;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.lcf.framework.exception.SysException;
import com.lcf.framework.utils.UserUtils;
import com.lcf.framework.vo.PageVO;
import com.lcf.system.dao.MovieDao;
import com.lcf.system.dto.MovieDTO;
import com.lcf.system.entity.*;
import com.lcf.system.query.MoviePageQuery;
import com.lcf.system.query.MovieRecommendPageQuery;
import com.lcf.system.service.MovieService;
import com.lcf.system.vo.MovieListVO;
import com.lcf.system.vo.MoviePerformerVO;
import com.lcf.system.vo.MovieTypeVO;
import com.lcf.system.vo.MovieVO;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static cn.hutool.core.bean.BeanUtil.copyProperties;

/**
 * 电影(Movie)表服务实现类
 *
 * @author lcf
 * @since 2025-05-07 22:23:21
 */
@Service("movieService")
public class MovieServiceImpl extends ServiceImpl<MovieDao, Movie> implements MovieService {
    @Resource
    private MovieDao movieDao;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(MovieDTO dto) {
        Movie movie = copyProperties(dto, Movie.class);
        movie.insert();

        if (CollectionUtils.isNotEmpty(dto.getMoviePerformers())) {
            BeanUtil.copyToList(dto.getMoviePerformers(), MoviePerformer.class).stream()
                    .peek(x -> x.setMovieId(movie.getId()))
                    .forEach(AbstractModel::insert);
        }

        if (CollectionUtils.isNotEmpty(dto.getMovieTypes())) {
            BeanUtil.copyToList(dto.getMovieTypes(), MovieType.class).stream()
                    .peek(x -> x.setMovieId(movie.getId()))
                    .forEach(AbstractModel::insert);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void edit(long id, MovieDTO dto) {
        Movie movie = getById(id);
        if (movie == null) {
            return;
        }

        BeanUtil.copyProperties(dto, movie);
        movie.updateById();

        Db.lambdaUpdate(MoviePerformer.class).eq(MoviePerformer::getMovieId, id).remove();
        if (CollectionUtils.isNotEmpty(dto.getMoviePerformers())) {
            BeanUtil.copyToList(dto.getMoviePerformers(), MoviePerformer.class).stream()
                    .peek(x -> x.setMovieId(movie.getId()))
                    .forEach(AbstractModel::insert);
        }

        Db.lambdaUpdate(MovieType.class).eq(MovieType::getMovieId, id).remove();
        if (CollectionUtils.isNotEmpty(dto.getMovieTypes())) {
            BeanUtil.copyToList(dto.getMovieTypes(), MovieType.class).stream()
                    .peek(x -> x.setMovieId(movie.getId()))
                    .forEach(AbstractModel::insert);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(long id) {
        removeById(id);

        Db.lambdaUpdate(MoviePerformer.class).eq(MoviePerformer::getMovieId, id).remove();
        Db.lambdaUpdate(MovieType.class).eq(MovieType::getMovieId, id).remove();
    }

    @Override
    public PageVO<MovieListVO> page(MoviePageQuery query) {
        long userId = UserUtils.getUserId();
        Page<Movie> page = Db.lambdaQuery(Movie.class)
                .eq(StringUtils.isNotBlank(query.getMovieName()), Movie::getMovieName, query.getMovieName())
                .eq(query.getMovieYear() != null, Movie::getMovieYear, query.getMovieYear())
                .orderByDesc(Movie::getCreateTime)
                .page(query.toPage());

        if (page != null && page.getRecords() != null && !page.getRecords().isEmpty()) {
            PageVO<MovieListVO> result = new PageVO<>(page, MovieListVO.class);

            List<Long> movieIdList = result.getList().stream().map(MovieListVO::getId).distinct().toList();
            List<MovieType> movieTypes = Db.lambdaQuery(MovieType.class).in(MovieType::getMovieId, movieIdList).list();
            Map<Long, List<MovieTypeVO>> map = movieTypes.stream().map(x -> BeanUtil.copyProperties(x, MovieTypeVO.class))
                    .collect(Collectors.groupingBy(MovieTypeVO::getMovieId));

            result.getList().forEach(x -> x.setMovieTypes(map.get(x.getId())));

            List<Long> collectMovieIdList = Db.lambdaQuery(MovieCollect.class)
                    .in(MovieCollect::getMovieId, movieIdList)
                    .eq(MovieCollect::getUserId, userId).list()
                    .stream().filter(x -> x.getIsCollect() == 1)
                    .map(MovieCollect::getMovieId).toList();

            List<Long> buyMovieIdList = Db.lambdaQuery(MovieBuy.class)
                    .eq(MovieBuy::getUserId, userId)
                    .in(MovieBuy::getMovieId, movieIdList)
                    .list()
                    .stream().map(MovieBuy::getMovieId).toList();

            List<Long> prizeMovieIdList = Db.lambdaQuery(MoviePrize.class)
                    .eq(MoviePrize::getUserId, userId)
                    .in(MoviePrize::getMovieId, movieIdList)
                    .list()
                    .stream().filter(x -> x.getIsPrize() == 1)
                    .map(MoviePrize::getMovieId).toList();

           List<Long> scoreMovieIdList = Db.lambdaQuery(MovieUserScore.class)
                    .eq(MovieUserScore::getUserId, userId)
                    .in(MovieUserScore::getMovieId, movieIdList)
                    .list().stream()
                            .map(MovieUserScore::getMovieId)
                                    .distinct().toList();

            result.getList().forEach(x -> {
                if (CollectionUtils.isNotEmpty(collectMovieIdList)) {
                    x.setIsCollect(collectMovieIdList.contains(x.getId()) ? 1 : 0);
                }
                if (CollectionUtils.isNotEmpty(buyMovieIdList)) {
                    x.setIsBuy(buyMovieIdList.contains(x.getId()) ? 1 : 0);
                }
                if (CollectionUtils.isNotEmpty(prizeMovieIdList)) {
                    x.setIsPrize(prizeMovieIdList.contains(x.getId()) ? 1 : 0);
                }
                if (CollectionUtils.isNotEmpty(scoreMovieIdList)) {
                    x.setIsScore(scoreMovieIdList.contains(x.getId()) ? 1 : 0);
                }
            });
            return result;
        }

        return new PageVO<>(page, MovieListVO.class);
    }

    @Override
    public MovieVO get(long id) {
        long userId = UserUtils.getUserId();

        Movie movie = getById(id);
        if (movie == null) {
            throw new SysException("电影不存在");
        }
        MovieVO result = BeanUtil.copyProperties(movie, MovieVO.class);

        List<MoviePerformer> moviePerformers = Db.lambdaQuery(MoviePerformer.class).eq(MoviePerformer::getMovieId, id).list();
        result.setMoviePerformers(BeanUtil.copyToList(moviePerformers, MoviePerformerVO.class));

        List<MovieType> movieTypes = Db.lambdaQuery(MovieType.class).eq(MovieType::getMovieId, id).list();
        result.setMovieTypes(BeanUtil.copyToList(movieTypes, MovieTypeVO.class));

        List<Long> collectMovieIdList = Db.lambdaQuery(MovieCollect.class)
                .eq(MovieCollect::getMovieId, id)
                .eq(MovieCollect::getUserId, userId).list()
                .stream().filter(x -> x.getIsCollect() == 1)
                .map(MovieCollect::getMovieId).toList();

        List<Long> buyMovieIdList = Db.lambdaQuery(MovieBuy.class)
                .eq(MovieBuy::getUserId, userId)
                .eq(MovieBuy::getMovieId, id)
                .list()
                .stream().map(MovieBuy::getMovieId).toList();

        List<Long> prizeMovieIdList = Db.lambdaQuery(MoviePrize.class)
                .eq(MoviePrize::getUserId, userId)
                .eq(MoviePrize::getMovieId, id)
                .list()
                .stream().filter(x -> x.getIsPrize() == 1)
                .map(MoviePrize::getMovieId).toList();

        List<Long> scoreMovieIdList = Db.lambdaQuery(MovieUserScore.class)
                .eq(MovieUserScore::getUserId, userId)
                .eq(MovieUserScore::getMovieId, id)
                .list().stream()
                .map(MovieUserScore::getMovieId)
                .distinct().toList();


        if (CollectionUtils.isNotEmpty(collectMovieIdList)) {
            result.setIsCollect(collectMovieIdList.contains(id) ? 1 : 0);
        }
        if (CollectionUtils.isNotEmpty(buyMovieIdList)) {
            result.setIsBuy(buyMovieIdList.contains(id) ? 1 : 0);
        }
        if (CollectionUtils.isNotEmpty(prizeMovieIdList)) {
            result.setIsPrize(prizeMovieIdList.contains(id) ? 1 : 0);
        }
        if (CollectionUtils.isNotEmpty(scoreMovieIdList)) {
            result.setIsScore(scoreMovieIdList.contains(result.getId()) ? 1 : 0);
        }

        return result;
    }

    @Override
    public PageVO<MovieListVO> recommend(MovieRecommendPageQuery query) {
        long userId = UserUtils.getUserId();
        List<String> typeList = movieDao.getUserCollectMovieType(userId);
        if (CollectionUtils.isEmpty(typeList)) {
            return new PageVO<>();
        }

        Page<Movie> page = movieDao.getMovieByType(query.toPage(), typeList);
        if (page != null && CollectionUtils.isNotEmpty(page.getRecords())) {
            List<Long> idList = page.getRecords().stream().map(Movie::getId).toList();
            List<Movie> list = Db.lambdaQuery(Movie.class).in(Movie::getId, idList).list();
            page.setRecords(list);
        }

        return new PageVO<>(page, MovieListVO.class);
    }
}

